/*
 * TCP protocol
 * Copyright (c) 2002 Fabrice Bellard
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#include "avformat.h"
#include "libavutil/avassert.h"
#include "libavutil/parseutils.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"

#include "internal.h"
#include "network.h"
#include "os_support.h"
#include "url.h"
#if HAVE_POLL_H
#include <poll.h>
#endif

typedef struct TCPJTT1078Context {
    const AVClass *class;
    URLContext *hd;
    int max_packet_size;        /**< if non zero, the stream is packetized with this max packet size */
    int fd;
    int listen;
    char *local_port;
    char *local_addr;
    int open_timeout;
    int rw_timeout;
    int listen_timeout;
    int recv_buffer_size;
    int send_buffer_size;
    int tcp_nodelay;
#if !HAVE_WINSOCK2_H
    int tcp_mss;
#endif /* !HAVE_WINSOCK2_H */
} TCPJTT1078Context;

#define OFFSET(x) offsetof(TCPJTT1078Context, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
    { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
    { "local_port",      "Local port",                                         OFFSET(local_port),     AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, .flags = D|E },
    { "local_addr",      "Local address",                                      OFFSET(local_addr),     AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, .flags = D|E },
    { "timeout",     "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "send_buffer_size", "Socket send buffer size (in bytes)",                OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "recv_buffer_size", "Socket receive buffer size (in bytes)",             OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "tcp_nodelay", "Use TCP_NODELAY to disable nagle's algorithm",           OFFSET(tcp_nodelay), AV_OPT_TYPE_BOOL, { .i64 = 1 },             0, 1, .flags = D|E },
#if !HAVE_WINSOCK2_H
    { "tcp_mss",     "Maximum segment size for outgoing TCP packets",          OFFSET(tcp_mss),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
#endif /* !HAVE_WINSOCK2_H */
    { NULL }
};

static const AVClass jtt1078tcp_class = {
    .class_name = "jtt1078tcp",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

static int jtt1078tcp_close(URLContext *h)
{
    TCPJTT1078Context *s = h->priv_data;
    if (s->hd)
        ffurl_closep(&s->hd);
    //closesocket(s->fd);
    return 0;
}

/* return non zero if error */
static int jtt1078tcp_open(URLContext *h, const char *uri, int flags)
{
    TCPJTT1078Context *rt = h->priv_data;
    char hostname[256], path[1024];
    int ret, port;
    uint8_t buf[2048];
    int mysckfd;
    h->max_packet_size = 1460;

    av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
                 path, sizeof(path), uri);
    av_log(h, AV_LOG_ERROR, "%s %d\n", hostname, port);

    mysckfd = socket(AF_INET, SOCK_STREAM, 0);
    if (mysckfd < 0)
    {
        return AVERROR(EIO);
    }
    
    struct sockaddr_in myser; 
    myser.sin_family = AF_INET; 
    myser.sin_port = htons(port); 
    myser.sin_addr.s_addr = inet_addr(hostname); 
    ret = connect(mysckfd, (struct sockaddr *)&myser, sizeof(myser)); 
    if(ret != 0) 
        return AVERROR(EIO);
    
    rt->fd = mysckfd;


                        
    /*
    ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, "?tcp_nodelay=%d", rt->tcp_nodelay);

    if ((ret = ffurl_open_whitelist(&rt->hd, buf, AVIO_FLAG_WRITE,
                                    &h->interrupt_callback, NULL,
                                    h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
        av_log(rt, AV_LOG_ERROR, "error\n");
        jtt1078tcp_close(h);
        return ret;
    }
    */
    return ret;
}


static int jtt1078tcp_write(URLContext *h, const uint8_t *buf, int size)
{
    av_log(h, AV_LOG_TRACE, "jtt1078tcp_write bufsize:%d\n", size);
    TCPJTT1078Context *s = h->priv_data;
    /*
    return ffurl_write(s->hd, buf, size);
    */
    int ret;

    ret = send(s->fd, buf, size, MSG_NOSIGNAL);
    return ret < 0 ? ff_neterrno() : ret;
}

static int jtt1078tcp_shutdown(URLContext *h, int flags)
{
    TCPJTT1078Context *s = h->priv_data;
    int how;

    if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
        how = SHUT_RDWR;
    } else if (flags & AVIO_FLAG_WRITE) {
        how = SHUT_WR;
    } else {
        how = SHUT_RD;
    }

    return shutdown(s->fd, how);
}


static int jtt1078tcp_get_file_handle(URLContext *h)
{
    TCPJTT1078Context *s = h->priv_data;
    return s->fd;
}

static int jtt1078tcp_get_window_size(URLContext *h)
{
    TCPJTT1078Context *s = h->priv_data;
    int avail;
    socklen_t avail_len = sizeof(avail);

#if HAVE_WINSOCK2_H
    /* SO_RCVBUF with winsock only reports the actual TCP window size when
    auto-tuning has been disabled via setting SO_RCVBUF */
    if (s->recv_buffer_size < 0) {
        return AVERROR(ENOSYS);
    }
#endif

    if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &avail, &avail_len)) {
        return ff_neterrno();
    }
    return avail;
}

const URLProtocol ff_jtt1078tcp_protocol = {
    .name                = "jtt1078tcp",
    .url_open            = jtt1078tcp_open,
    .url_write           = jtt1078tcp_write,
    .url_close           = jtt1078tcp_close,
    .url_get_file_handle = jtt1078tcp_get_file_handle,
    .url_get_short_seek  = jtt1078tcp_get_window_size,
    .url_shutdown        = jtt1078tcp_shutdown,
    .priv_data_size      = sizeof(TCPJTT1078Context),
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
    .priv_data_class     = &jtt1078tcp_class,
    .default_whitelist = "tcp",
};
